﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace CityGateway.Objects
{
    [DataContract]
    [Serializable]
   public  class CityStatus
    {


        public CityStatus()
        {
            this.XP = 0;
            this.Cash = 100000;
            this.MetropolineCash = 100;
            this.Population = new Population();
            this.CityDate = new DateTime(1920, 1, 1);
            this.Population.PopulationByYears.Add(1900, 2000);
            this.Population.PopulationByYears.Add(1901, 2000);
            this.Population.PopulationByYears.Add(1905, 200);
            this.Population.PopulationByYears.Add(1910, 200);

            this.Age = 0;

            TaxHistory = new Dictionary<int, int>();
            BillsHistory = new Dictionary<int, int>();
            Messages = new Dictionary<DateTime, string>();
            CityScore = new CityScore();
        }

        [DataMember]
        public CityScore CityScore { get; set; }

        [DataMember]
        public DateTime CityDate { get; set; }

        

        [DataMember]
        public int ResidentialAvailable { get; set; }

        [DataMember]
        public int ResidentialNeed { get; set; }

        [DataMember]
        public int CommercialAvailable { get; set; }

        [DataMember]
        public int IndustrialAvailable { get; set; }
       

        [DataMember]
        public int JobsAvailable { get; set; }

        [DataMember]
        public int JobsNeed { get; set; }



      

      





        [DataMember]
        public int XP { get; set; }

        [DataMember]
        public int Cash { get; set; }

        [DataMember]
        public int MetropolineCash { get; set; }

        [DataMember]
        public Population Population { get; set; }

        [DataMember]
        public int Age { get; set; }






        [DataMember]
        public Dictionary<int,int> BillsHistory { get; set; }

        [DataMember]
        public Dictionary<int, int> TaxHistory { get; set; }


        public void CalculateStatus(City city)
        {
            int currentYear = CityDate.Year;

            #region job count
            

            //calculate how many people need jobs
            List<int> workersCount = (from w in Population.PopulationByYears.Keys
                                      where w <= currentYear - 20
                                      select w).ToList();

            foreach (int key in workersCount)
            {
                JobsNeed += Population.PopulationByYears[key];
            }


            ResidentialNeed = (int)JobsNeed / 2;

            //count jobs
            int jobCount=0;
            JobsNeed = 0;
            List<CityElement> elements = (from elem in city.Elements
                                          where elem.ElementType == CityElementType.Commercial || elem.ElementType == CityElementType.Industrial
                                          select elem).ToList();

            foreach (CityElement element in elements)
            {
                jobCount += element.Capacity;              

            }

            CityScore.Jobs = jobCount - JobsNeed;
            #endregion

            JobsAvailable = jobCount;

       





            #region Flat count
            //count jobs
            int flatCount = 0;
             elements = (from elem in city.Elements
                                          where elem.ElementType == CityElementType.Residential 
                                          select elem).ToList();

            foreach (CityElement element in elements)
            {
                flatCount += element.Capacity;
            }
            CityScore.Residential = flatCount - ResidentialNeed;
            #endregion

            this.ResidentialAvailable = flatCount;




            #region healthCare capacity

            //count jobs
            int healthCount = 0;
            elements = (from elem in city.Elements
                        where elem.ElementType == CityElementType.HealthCare
                        select elem).ToList();

            foreach (CityElement element in elements)
            {
                healthCount += element.Capacity;          
            }

            CityScore.Health = healthCount;
            #endregion



            #region Energy capacity

            //count jobs
            int energyCount = 0;
            elements = (from elem in city.Elements
                        where elem.ElementType == CityElementType.Energy
                        select elem).ToList();

            foreach (CityElement element in elements)
            {
                energyCount += element.Capacity;
            }
            CityScore.Energy = energyCount;
            #endregion




            #region Waste capacity

           //get the amount of waste handled
            int wasteCount = 0;
            elements = (from elem in city.Elements
                        where elem.ElementType == CityElementType.Waste
                        select elem).ToList();

            foreach (CityElement element in elements)
            {
                wasteCount += element.Capacity;
            }

            CityScore.WasteHandled = wasteCount;



            //get the amount of waste produced
              wasteCount = 0;
            elements = (from elem in city.Elements
                        where elem.Polution >0
                        select elem).ToList();

            foreach (CityElement element in elements)
            {
                wasteCount += element.Polution;
            }

            CityScore.WasteProduced = wasteCount;

            #endregion




            #region Police capacity

            //count jobs
            int policeCount = 0;
            elements = (from elem in city.Elements
                        where elem.ElementType == CityElementType.Police
                        select elem).ToList();

            foreach (CityElement element in elements)
            {
                policeCount += element.Capacity;
            }

            CityScore.Security = policeCount;
            #endregion



            #region Education capacity

            //count jobs
            int educationCount = 0;
            elements = (from elem in city.Elements
                        where elem.ElementType == CityElementType.Education
                        select elem).ToList();

            foreach (CityElement element in elements)
            {
                educationCount += element.Capacity;
                
            }
            CityScore.Education = educationCount;
            #endregion

        }

        public void CalculateJobs(City city, ref  Dictionary<int, int> evacs)
        {
          int currentYear = CityDate.Year;

          if (city.CityStatus.JobsNeed > city.CityStatus.JobsAvailable)
            {
                //some people need to leave this town now
                int amountToMove = city.CityStatus.JobsNeed - city.CityStatus.JobsAvailable;

                //calculate how many people need jobs
                List<int> workersYearKeys = (from w in Population.PopulationByYears.Keys
                                             where w >= currentYear - 65 && w <= currentYear - 20
                                             select w).ToList();


                //devide the amount leavers in the number of year keys

                int takeFromEachYear = 0;
                amountToMove = (int)Math.Ceiling((double)amountToMove);
                if (amountToMove > 0)
                {
                 
                    takeFromEachYear = (int)amountToMove / workersYearKeys.Count;
                    HandleImmigraitionNumbers(workersYearKeys, takeFromEachYear, amountToMove, ref   evacs);

                }

  
            }
        

        }

        public void CalculateResidentials(City city, ref  Dictionary<int, int> evacs)
        {
            int currentYear = CityDate.Year;

            if (city.CityStatus.ResidentialNeed > city.CityStatus.ResidentialAvailable)
            {
                //some people need to leave this town now
                int amountToMove = city.CityStatus.ResidentialNeed - city.CityStatus.ResidentialAvailable;

                //calculate how many people need jobs
                List<int> takefrom = (from w in Population.PopulationByYears.Keys
                                             where w <= currentYear - 20
                                             select w).ToList();


                //devide the amount leavers in the number of year keys

                int takeFromEachYear = 0;
                amountToMove = (int)Math.Ceiling((double)amountToMove);
                if (amountToMove > 0)
                {
                  
                    takeFromEachYear = (int)amountToMove / takefrom.Count;
                    HandleImmigraitionNumbers(takefrom, takeFromEachYear, amountToMove, ref   evacs);
                }

             

            }


        }


        public void HandleImmigraitionNumbers(List<int> keys, int takeFromEachYear, int amountToMove, ref  Dictionary<int, int> evacs)
        {


            foreach (int key in keys)
            {


             
                    if (Population.PopulationByYears[key] > takeFromEachYear)
                    {
                        if (evacs.ContainsKey(key))
                            evacs[key] += takeFromEachYear;
                        else
                            evacs.Add(key, takeFromEachYear);

                        Population.PopulationByYears[key] = Population.PopulationByYears[key] - takeFromEachYear;
                        amountToMove -= takeFromEachYear;
                    }
                    else
                    {
                        if (evacs.ContainsKey(key))
                            evacs[key] += Population.PopulationByYears[key];
                        else
                            evacs.Add(key, Population.PopulationByYears[key]);

                        amountToMove -= Population.PopulationByYears[key];
                        Population.PopulationByYears[key] = Population.PopulationByYears[key] - Population.PopulationByYears[key];
                    }
               

                //JobsNeed += city.Population.PopulationByYears[key];
            }
        }


        public void CalculateProcreation()
        {
            int currentYear = CityDate.Year;
            //calculate how many people need jobs
            List<int> birthYearKeys = (from w in Population.PopulationByYears.Keys
                                       where w <= currentYear - 20 && w >= currentYear - 50
                                       select w).ToList();

            //produce 10% growth with no factor
            foreach (int key in birthYearKeys)
            {

                //give birth
                if (!Population.PopulationByYears.ContainsKey(currentYear))
                    Population.PopulationByYears.Add(currentYear, 0);

                int birthRate = 500;
                Population.PopulationByYears[currentYear] += (int)Math.Ceiling((double)Population.PopulationByYears[key] / birthRate);

            }
        }

        public Dictionary<int, int> AdjustNeedsAndPopulation(City city)
        {
             Dictionary<int, int> evacs = new Dictionary<int,int>();


             CalculateResidentials(city,ref  evacs);
             CalculateJobs(city, ref evacs);

             CalculateProcreation();

            return evacs;

        }


        public void CollectTax(City city)
        {
            int totalTax = 0;

            #region Calculate residential
            List<CityElement> elements = (from elem in city.Elements
                                          where elem.ElementType == CityElementType.Residential
                                          select elem).ToList();

            foreach(CityElement c in elements)
                    totalTax+=  city.Budget.Residential * c.Capacity;


     

#endregion


               elements = (from elem in city.Elements
                           where elem.ElementType == CityElementType.Commercial
                                            select elem).ToList();
               foreach (CityElement c in elements)
                 totalTax += city.Budget.Commercial * c.Capacity;


 
               elements = (from elem in city.Elements
                           where elem.ElementType == CityElementType.Industrial
                                            select elem).ToList();

               foreach (CityElement c in elements)
               totalTax += city.Budget.Industrial * c.Capacity;

 



              if (TaxHistory == null)
                  TaxHistory = new Dictionary<int, int>();

              if (TaxHistory.ContainsKey(this.CityDate.Year))
                  TaxHistory[this.CityDate.Year] += totalTax;
              else
                  TaxHistory.Add(this.CityDate.Year, totalTax);


              this.Cash += totalTax;


        }


        public void PayTheBills(City city)
        {

            int totalByBudget = 0;

            totalByBudget += city.Budget.Education;
            totalByBudget += city.Budget.Energy;
            totalByBudget += city.Budget.HealthCare;
            totalByBudget += city.Budget.Police;
            totalByBudget += city.Budget.Transportation;
            totalByBudget += city.Budget.Waste;
            totalByBudget += city.Budget.Water;

            if (BillsHistory == null)
                BillsHistory = new Dictionary<int, int>();

            if (BillsHistory.ContainsKey(this.CityDate.Year))
                BillsHistory[this.CityDate.Year] += totalByBudget;
            else
                BillsHistory.Add(this.CityDate.Year, totalByBudget);


            this.Cash -= totalByBudget;


        }



        public Dictionary<DateTime, string> Messages { get; set; }


    }
}
